/**
 * <copyright>
 * </copyright>
 *
 * $Id$
 */
package PlasticSystem.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import java.util.Set;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.impl.EObjectImpl;
import org.eclipse.emf.ecore.util.InternalEList;
import org.plastic.mrn.*;
import org.plastic.util.*;
import org.plastic.mrn.QoS.*;
import java.util.*;
import cn.edu.pku.sei.ra.wrapping.CommonWrapping.WrappingEObject;
import org.plastic.mrn.MNClient;
import org.plastic.mrn.MulticastPacketHandler;
import org.plastic.mrn.PlasticAddress;
import org.plastic.mrn.PlasticPacketInputStream;
import org.plastic.mrn.QoS.QoSInfo;
import org.plastic.mrn.QoS.QoSParameter;
import org.plastic.util.PlasticLogging;

import PlasticSystem.Local;
import PlasticSystem.PlasticSystemPackage;
import PlasticSystem.Remote;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import java.util.Collection;
import cn.edu.pku.sei.ra.wrapping.CommonWrapping.impl.WrappingEObjectImpl;
import cn.edu.pku.sei.ra.wrapping.CommonWrapping.util.EObjectContainmentEListForWrapping;

/**
 * <!-- begin-user-doc --> An implementation of the model object '
 * <em><b>Local</b></em>'. <!-- end-user-doc -->
 * <p>
 * The following features are implemented:
 * <ul>
 *   <li>{@link PlasticSystem.impl.LocalImpl#getName <em>Name</em>}</li>
 *   <li>{@link PlasticSystem.impl.LocalImpl#getMaxThreads <em>Max Threads</em>}</li>
 *   <li>{@link PlasticSystem.impl.LocalImpl#getRemote <em>Remote</em>}</li>
 * </ul>
 * </p>
 *
 * @generated
 */
public class LocalImpl extends WrappingEObjectImpl implements Local {
	/**
   * The default value of the '{@link #getName() <em>Name</em>}' attribute.
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @see #getName()
   * @generated
   * @ordered
   */
	protected static final String NAME_EDEFAULT = null;

	/**
   * The cached value of the '{@link #getName() <em>Name</em>}' attribute.
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @see #getName()
   * @generated
   * @ordered
   */
	protected String name = NAME_EDEFAULT;

	/**
   * The default value of the '{@link #getMaxThreads() <em>Max Threads</em>}' attribute.
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @see #getMaxThreads()
   * @generated
   * @ordered
   */
	protected static final int MAX_THREADS_EDEFAULT = 0;

	/**
   * The cached value of the '{@link #getMaxThreads() <em>Max Threads</em>}' attribute.
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @see #getMaxThreads()
   * @generated
   * @ordered
   */
	protected int maxThreads = MAX_THREADS_EDEFAULT;

	/**
   * The cached value of the '{@link #getRemote() <em>Remote</em>}' containment reference list.
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @see #getRemote()
   * @generated
   * @ordered
   */
	protected EList<Remote> remote;

	/**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	protected LocalImpl() {
    super();
  }

	/**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	@Override
	protected EClass eStaticClass() {
    return PlasticSystemPackage.Literals.LOCAL;
  }

	private static MNClient localListener = null;

	private static MNClient broadcast = null;

	private static QoSInfo defaultQos = null;

	private static HashMap remotes = new HashMap();

	public static final Integer INVITE = 0;

	public static final Integer JOIN = 1;

	class BroadcastThread implements Runnable {
		MNClient mrn = null;
		PlasticAddress pa = null;
		String groupName = null;

		private final PlasticLogging log = new PlasticLogging(
				BroadcastThread.class.getName());

		public BroadcastThread(MNClient mrn, String groupName, PlasticAddress pa) {
			this.mrn = mrn;
			this.pa = pa;
			this.groupName = groupName;
		}

		@Override
		public void run() {
			try {
				boolean run = true;
				Vector ip = new Vector();
				ip.add(INVITE);
				ip.add(this.pa);
				while (run) {
					mrn.multicast(ip, groupName);
					// sleeps for a while
					Thread.sleep(50000);
				}
				// unregister from the MultiRadioNetworkingDaemon
				mrn.unregister();

				log.info("Shutting down the server");
				mrn.shutdown();

			} catch (Exception e) {
				log.error("", e);
			}

		}

	}

	class ListenThread implements Runnable {
		MNClient mrn = null;
		HashMap clients = null;

		private final PlasticLogging log = new PlasticLogging(
				ListenThread.class.getName());

		public ListenThread(MNClient mrn, HashMap clients) {
			this.mrn = mrn;
			this.clients = clients;
		}

		@Override
		public void run() {
			try {
				boolean run = true;

				while (run) {
					// Waits for a connection
					PlasticPacketInputStream in1 = mrn.accept();
					Thread.sleep(1000);
					Vector in = (Vector) in1.readObject();

					log.info("received:" + in);
					
					if (((Integer) in.get(0)).equals(JOIN)) {
						try {							
							String key = (String) in.get(1);
							Vector value = new Vector();
							value.add(key);
							value.add(mrn.getMyPlasticAddress());//my pa
							value.add((PlasticAddress) in.get(2));//it's pa
							value.add((Integer) in.get(3));		//potentialThread
							clients.put(key, value);
							log.info("new client joint:" + key);
						} catch (Exception e) {
							log.error("", e);
						}
					}
					// sleeps for a while
					Thread.sleep(200);
				}
				// unregister from the MultiRadioNetworkingDaemon
				mrn.unregister();

				log.info("Shutting down the server");
				mrn.shutdown();

			} catch (Exception e) {
				log.error("", e);
			}
		}
	}

	/**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	public Object lookupCore() {
    Object result=null;
    if (maxThreads <= 0)
      maxThreads = 3;
    if (name == null || name.length() == 0)
      name = "PlasticServer";
    if (defaultQos == null) {
      defaultQos = new QoSInfo();
      defaultQos.setBitrate(QoSParameter.MID);
      defaultQos.setTransferDelay(QoSParameter.MID);
      defaultQos.setPacketLoss(QoSParameter.MID);
      // qos.setMobility(QoSParameter.NULL);
      defaultQos.setPrice(QoSParameter.NULL);
      defaultQos.setPowerConsumption(QoSParameter.MID);
      defaultQos.setSignalStrength(QoSParameter.MID);
    }
    if (localListener == null) {
      localListener = new MNClient(name + "Listener", maxThreads);
      try {
        localListener.setMaster(8090);
        localListener.activateBestNetworks(defaultQos, 0.5);
        boolean r = localListener.register();

        ListenThread lt = new ListenThread(localListener, remotes);
        Thread t = new Thread(lt);
        t.start();

        Thread.sleep(100);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    if (broadcast == null) {
      broadcast = new MNClient(name + "Broadcast", 1);
      try {
        broadcast.setMaster(8090);
        broadcast.activateBestNetworks(defaultQos, 0.5);
        boolean r = broadcast.register();

        BroadcastThread bt = new BroadcastThread(broadcast, name+"Group",
            localListener.getMyPlasticAddress());
        Thread t = new Thread(bt);
        t.start();

        Thread.sleep(100);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    return result;
  }

	/**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	public List listSubCores(int featureID) {
    List result=new ArrayList();
    switch(featureID){
  
  
    case PlasticSystemPackage.LOCAL__REMOTE:
    	HashMap remotes = new HashMap();
    	ListenThread lt = new ListenThread(mncListner, remotes);
        new Thread(lt).start();
        BroadcastThread bt = new BroadcastThread(mncBroadcast, 
        		name+"Group", mncListner.getMyPlasticAddress());
        new Thread(bt).start();
        Thread.sleep(100);
        result = new ArrayList(remotes.values());
    break;
    
  }
    return result;
  }

	/**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	public EClass getChildClassMap(Object object) {
    PlasticSystemPackage pack=PlasticSystemPackage.eINSTANCE;	
  
  
  
    return null;
  }

	/**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	public String getName() {
        Object core=(Object)getCore();
        if(core==null) return name;	
        
    return name; 
  }

	/**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	public void setName(String newName) {
    String oldName = name;
    name = newName;
        	
      Object core=(Object)getCore();
      if(core==null) return;
      	
        
    if (eNotificationRequired())
      eNotify(new ENotificationImpl(this, Notification.SET, PlasticSystemPackage.LOCAL__NAME, oldName, name));
  }

	/**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	public int getMaxThreads() {
        Object core=(Object)getCore();
        if(core==null) return maxThreads;	
        
    return maxThreads; 
  }

	/**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	public void setMaxThreads(int newMaxThreads) {
    int oldMaxThreads = maxThreads;
    maxThreads = newMaxThreads;
        	
      Object core=(Object)getCore();
      if(core==null) return;
      	
        
    if (eNotificationRequired())
      eNotify(new ENotificationImpl(this, Notification.SET, PlasticSystemPackage.LOCAL__MAX_THREADS, oldMaxThreads, maxThreads));
  }

	/**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	public EList<Remote> getRemote() {

    
    if (remote == null)
    {
  
  
    
      remote = new EObjectContainmentEListForWrapping<Remote>(
          Remote.class, 
          this, 
          PlasticSystemPackage.LOCAL__REMOTE,
          PlasticSystemPackage.eINSTANCE.getRemote()
        );
        
  }
  
    ((EObjectContainmentEListForWrapping<Remote>)remote)
    .refreshWrap();
  
    return remote;
  }

	/**
   * <!-- begin-user-doc -->
   * <!-- end-user-doc -->
   * @generated
   */
  @SuppressWarnings("unchecked")
  @Override
  public NotificationChain eInverseAdd(InternalEObject otherEnd, int featureID, NotificationChain msgs)
  {
    switch (featureID)
    {
      case PlasticSystemPackage.LOCAL__REMOTE:
        return ((InternalEList<InternalEObject>)(InternalEList<?>)getRemote()).basicAdd(otherEnd, msgs);
    }
    return super.eInverseAdd(otherEnd, featureID, msgs);
  }

  /**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	@Override
	public NotificationChain eInverseRemove(InternalEObject otherEnd,
			int featureID, NotificationChain msgs) {
    switch (featureID)
    {
      case PlasticSystemPackage.LOCAL__REMOTE:
        return ((InternalEList<?>)getRemote()).basicRemove(otherEnd, msgs);
    }
    return super.eInverseRemove(otherEnd, featureID, msgs);
  }

	/**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	@Override
	public Object eGet(int featureID, boolean resolve, boolean coreType) {
    switch (featureID)
    {
      case PlasticSystemPackage.LOCAL__NAME:
        return getName();
      case PlasticSystemPackage.LOCAL__MAX_THREADS:
        return new Integer(getMaxThreads());
      case PlasticSystemPackage.LOCAL__REMOTE:
        return getRemote();
    }
    return super.eGet(featureID, resolve, coreType);
  }

	/**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	@SuppressWarnings("unchecked")
	@Override
	public void eSet(int featureID, Object newValue) {
    switch (featureID)
    {
      case PlasticSystemPackage.LOCAL__NAME:
        setName((String)newValue);
        return;
      case PlasticSystemPackage.LOCAL__MAX_THREADS:
        setMaxThreads(((Integer)newValue).intValue());
        return;
      case PlasticSystemPackage.LOCAL__REMOTE:
        getRemote().clear();
        getRemote().addAll((Collection<? extends Remote>)newValue);
        return;
    }
    super.eSet(featureID, newValue);
  }

	/**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	@Override
	public void eUnset(int featureID) {
    switch (featureID)
    {
      case PlasticSystemPackage.LOCAL__NAME:
        setName(NAME_EDEFAULT);
        return;
      case PlasticSystemPackage.LOCAL__MAX_THREADS:
        setMaxThreads(MAX_THREADS_EDEFAULT);
        return;
      case PlasticSystemPackage.LOCAL__REMOTE:
        getRemote().clear();
        return;
    }
    super.eUnset(featureID);
  }

	/**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	@Override
	public boolean eIsSet(int featureID) {
    switch (featureID)
    {
      case PlasticSystemPackage.LOCAL__NAME:
        return NAME_EDEFAULT == null ? name != null : !NAME_EDEFAULT.equals(name);
      case PlasticSystemPackage.LOCAL__MAX_THREADS:
        return maxThreads != MAX_THREADS_EDEFAULT;
      case PlasticSystemPackage.LOCAL__REMOTE:
        return remote != null && !remote.isEmpty();
    }
    return super.eIsSet(featureID);
  }

	/**
   * <!-- begin-user-doc --> <!-- end-user-doc -->
   * @generated
   */
	@Override
	public String toString() {
    if (eIsProxy()) return super.toString();

    StringBuffer result = new StringBuffer(super.toString());
    result.append(" (name: ");
    result.append(name);
    result.append(", maxThreads: ");
    result.append(maxThreads);
    result.append(')');
    return result.toString();
  }

} // LocalImpl
